package org.smartboot.sosa.core.rmi;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang3.math.NumberUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.smartboot.sosa.core.util.AssertUtils;
import org.smartboot.sosa.rpc.RpcConfig;
import org.smartboot.sosa.rpc.RpcRequest;
import org.smartboot.sosa.rpc.RpcResponse;
import org.smartboot.sosa.rpc.RpcServiceCallback;
import org.smartboot.sosa.rpc.RpcUnit;
import org.smartboot.sosa.rpc.channel.ChannelService;
import org.smartboot.sosa.rpc.netty.server.RpcNettyServer;

/**
 * RMI服务端
 * 
 * @author Seer
 * @version RmiServer.java, v 0.1 2015年11月24日 下午3:02:46 Seer Exp.
 */
public class RmiServer {
	private static final Logger LOGGER = LogManager.getLogger(RmiServer.class);
	private ChannelService server;

	private RmiServiceCallback rmiCallback;

	private Properties properties;
	// QuickMonitorTimer timer = new QuickMonitorTimer();
	/** 基础数据类型 */
	private Map<String, Class<?>> primitiveClass = new HashMap<String, Class<?>>();

	{
		primitiveClass.put("int", int.class);
		primitiveClass.put("double", double.class);
		primitiveClass.put("long", long.class);
	}

	public void init() throws Exception {
		rmiCallback = new RmiServiceCallback();

		RpcConfig rpcConfig = new RpcConfig();
		rpcConfig.setRpcCallback(rmiCallback);
		rpcConfig.setHost(properties.getProperty(RmiProperties.HOST));
		rpcConfig.setPort(NumberUtils.toInt(properties.getProperty(RmiProperties.PORT)));

		server = new RpcNettyServer();
		// server = new RpcSocketServer();
		server.start(rpcConfig);
		LOGGER.info("start rmi server success,port:" + NumberUtils.toInt(properties.getProperty(RmiProperties.PORT))
			+ ",host:" + properties.getProperty(RmiProperties.HOST));
	}

	public void publishService(String key, Object service) {
		rmiCallback.put(key, service);
	}

	public void destory() {
		LOGGER.info("stopping rmi server");
		server.shutdown();
		LOGGER.info("stop rmi server success");
	}

	/**
	 * Setter method for property <tt>properties</tt>.
	 *
	 * @param properties
	 *            value to be assigned to property properties
	 */
	public final void setProperties(Properties properties) {
		this.properties = properties;
	}

	class RmiServiceCallback implements RpcServiceCallback {

		private Map<String, Object> impMap = new HashMap<String, Object>();

		public void put(String key, Object obj) {
			impMap.put(key, obj);
		}

		@SuppressWarnings("unchecked")
		public <T> void callback(T t) throws Exception {
			RpcUnit<RpcRequest> unit = (RpcUnit<RpcRequest>) t;
			RpcRequest req = unit.getUnit();
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug(req);
			}
			RpcResponse resp = new RpcResponse(req.getUuid());
			try {
				String[] paramClassList = req.getParamClassList();
				Object[] paramObjList = req.getParams();
				// 获取入参类型
				Class<?>[] classArray = null;
				if (paramClassList != null) {
					classArray = new Class[paramClassList.length];
					for (int i = 0; i < classArray.length; i++) {
						Class<?> clazz = primitiveClass.get(paramClassList[i]);
						if (clazz == null) {
							classArray[i] = Class.forName(paramClassList[i]);
						} else {
							classArray[i] = clazz;
						}
					}
				}
				// 调用接口
				Object impObj = impMap.get(req.getInterfaceClass());
				AssertUtils.notNull(impObj, "can not find interface: " + req.getInterfaceClass());
				Method method = impObj.getClass().getMethod(req.getMethod(), classArray);
				Object obj = method.invoke(impObj, paramObjList);
				resp.setReturnObject(obj);
				resp.setReturnType(method.getReturnType().getName());
			} catch (InvocationTargetException e) {
				LOGGER.catching(e);
				resp.setException(e.getTargetException().getMessage());
			} catch (Exception e) {
				LOGGER.catching(e);
				resp.setException(e.getMessage());
			}
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug(resp);
			}
			unit.getSession().sendResponse(resp);
			// timer.processFilter();
		}

	}
}
